package org.swinglife.log.webapi;


import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by zhufe on 2017/9/25.
 * 日志切面
 */
@Aspect
@Component
public class SLLogInterceptor {

    //我的日志框架还未完成
//    private SwingLifeLog logger = new SwingLifeLog("/opt/log","aop_webapi");

    private Logger logger= LoggerFactory.getLogger(SLLogInterceptor.class);

    /***
     * 定义切面点 SLLog注解
     */
    @Pointcut("@annotation(org.swinglife.log.webapi.SLLog)")
    private void anyMethod() {}


    /***
     * 获取参数名称及返回值
     * @param cls
     * @param clazzName
     * @param methodName
     * @param args
     * @return
     */
    private Map<String,Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String,Object > map=new HashMap<String,Object>();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);

        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
        }
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++){
            map.put( attr.variableName(i + pos),args[i]);//paramNames即参数名
        }
        return map;
    }

    /***
     * 获取方法参数信息
     * @param joinPoint
     * @throws Throwable
     */
    private void getInfo(ProceedingJoinPoint joinPoint,String methoName) throws Throwable {
        System.out.println("测试2");
        String classType = joinPoint.getTarget().getClass().getName();
        Class<?> clazz = Class.forName(classType);
        String clazzName = clazz.getName();
        String methodName = joinPoint.getSignature().getName(); //获取方法名称
        Object[] args = joinPoint.getArgs();//参数
        //获取参数名称和值
        final Map<String,Object > nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName,args);
        String argsData = ""; //方法参数
        for (String key : nameAndArgs.keySet()) {
            argsData += key+"="+nameAndArgs.get(key)+", ";
        }
        logger.info("[========3."+methodName+" 方法参数:"+argsData+"========]");
        logger.info("[========4."+methodName+" 参数结束========]");
    }

    /***
     * 获取SLLog注解内信息
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    private SLLog getAnnotationInfoBySllog(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        Method method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(),
                ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes());
        SLLog slLog = method.getAnnotation(SLLog.class);
        return slLog;
    }

    /**
     * 产生日志
     * @param pjp
     */
    private Object printLogAndRunMethod(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("测试1");
        long startTime=System.currentTimeMillis();   //获取开始时间
        logger.info("[========1.开始调用: "+pjp.getTarget().getClass().getName()+"========]");
        String methodName = pjp.getSignature().getName();   //获取方法名称
        logger.info("[========2.调用:"+methodName+" 方法========]");
        getInfo(pjp,methodName);
        Object object = pjp.proceed();//执行该方法
        logger.info("[========5." + methodName + " 方法返回值: " + object + "========]");
        long endTime=System.currentTimeMillis(); //获取结束时间
        logger.info("[========6."+methodName+" 方法结束，耗时"+(endTime-startTime)+"ms========]");
        if(methodName==oldMethodName){
            excCount++;
        }else{
            excCount = 1;
        }
        oldMethodName = methodName;
        if(excCount>=2){
            logger.info("!!!!!!!!!警告：【"+methodName+"】该方法被连续调用:"+excCount+"次!!!!!!!!");
        }
        logger.info("\n\n");
        return object;
    }


    /***
     * 环绕切面
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("anyMethod()")
    public Object doPrintLogAnyMehodBySLLog(ProceedingJoinPoint pjp) throws Throwable{
        SLLog slLog = this.getAnnotationInfoBySllog(pjp);
        System.out.println("测试");
        if(slLog!=null&&slLog.content()!=""){
            logger.info("[========SLLOG:"+slLog.content()+"========]");
        }
        return this.printLogAndRunMethod(pjp);
    }





    //调用次数
    private int excCount = 0;
    //上一次调用的方法名称
    private String oldMethodName = "";

}